<?php

/**
 * Celebrio Database module
 * 
 * @copyright  Copyright (c) 2011 Celebrio Software
 * @package    Celebrio
 * @subpackage Database
 */

namespace Celebrio\Database;

use Nette\Object;
use Nette\Environment;
use dibi;
use DibiException;
use Celebrio\Core\CriticalException;
use \Celebrio\Core\DummyInit;
use Celebrio\Core\DDL;
use Celebrio\Core\ServiceProvider;
use Celebrio\AccessControl\AclConstants;
use Celebrio\PasswordHasher;
use Celebrio\AccessControl\UserManagement;

use Celebrio\AccessControl\AlbireoUser;

/**
 * Contains the common functionality of dibi-based Celebrio database
 * modules.
 *
 * @author     Albireo Solutions
 * @package    Kernel
 */
abstract class DatabaseInit extends DummyInit {

    private $userManagement;

    public function __construct() {
        $this->userManagement = new UserManagement();
    }

    /**
     * Connects dibi to the database with the given settings.
     * 
     * @param array $settings 
     */
    public function init(array $settings) {

        if ($settings == null)
            throw new \Exception("null settings");

        if (dibi::isConnected()) {
            dibi::disconnect();
        }

        $user = Environment::getUser();
        $oldSettings = $settings;
        if ($user->isLoggedIn()) {
            try {
                $dbPassword = PasswordHasher::databaseHash(
                                $user->getIdentity()->getName(),
                                $user->getIdentity()->password
                );

                // try log to DB with user-specific credentials
                $this::setUser($settings, $user->getIdentity()->getName());
                $this::setPassword($settings, $dbPassword);
                //$this::setDatabase($settings, "celebrio");
                //\Nette\Debug::dump($settings);
                dibi::connect($settings);
                return;
            } catch (DibiException $e) {
                // user does not exist in DB or DB is down
                // try log to DB with master credentials several lines below
            }
        }

        try {
            dibi::connect($oldSettings);
        } catch (DibiException $e) {
            // even the master credentials failed, DB is probably down
            throw new CriticalException(_("Kernel is in Critical Mode due to Database system unavailability."), $e->getCode(), $e);
        }
    }

    /**
     * Performs the installation of database. 
     * 
     * @param type $configuration
     * @param type $driverName 
     */
    public function installDatabase(array $configuration) {
        $databaseName = $this::getDatabase($configuration);

        // connect to the general database (config user) and create the database
        $this::setMasterDatabase($configuration);
        $this->init($configuration);
        $this->createDB($configuration, $databaseName); // master u Azure

        dibi::disconnect();

        // connect to the currently created database (config user) and create
        // tables + ROOT_USER login role
        $this::setDatabase($configuration, $databaseName);
        $this->init($configuration);
        $this->createTables($databaseName);
        $this->createRoot();

//        NO NEEDED ANYMORE (ACL IS HANDLED BY ACL MODULE)
//        dibi::disconnect();
//        
//        // connect to the currently created database as ROOT_USER and create ACL
//        $rootName = AclConstants::ROOT_USER;
//        $this::setUser($configuration, $rootName);
//        $this::setPassword($configuration, $this->databasePassword($rootName, PasswordHasher::userHash($rootName, "albireo")));
//        $this->init($configuration);
//        $this->createAcl();
    }

    public function formFields() {
        return array(
            array("name" => "host", "label" => "Host:", "type" => "text", "required" => _('Please provide a host name.')),
            array("name" => "username", "label" => "User:", "type" => "text", "required" => _('Please provide a username.')),
            array("name" => "password", "label" => "Password:", "type" => "password", "required" => _('Please provide a password.')),
            array("name" => "database", "label" => "Database:", "type" => "text", "required" => _('Please provide a database name.')),
        );
    }

    /**
     * Creates database
     *
     * @param array $configuration
     * @param string $databaseName 
     */
    private function createDB($configuration, $databaseName) {
        //TODO: there are 2 extreme ways
        // 1st: throw exception when db exists... but why?
        // 2nd: install even when DB exists but user should know it
        // THE ideal way is to ask user whether he wants to proceed anyway
        //THIS LINE HAS TO BE REMOVED/////////////////
        //dibi::query("DROP DATABASE " . $databaseName);
        //////////////////////////////////////////////

        if (!$this->databaseExists($databaseName)) {
            dibi::query("CREATE DATABASE " . $databaseName);
            //throw new \Exception("Database already exists.");
        }
        //dibi::query("CREATE DATABASE ".$databaseName);
    }

    /**
     * Creates database tables
     *
     * @param IDatabaseModule $module
     * @param array $values
     */
    private function createTables($dbName) {
        if ($this->tablesExist($dbName)) {
            throw new \Exception("Tables already exist");
        }

        $dbname = ServiceProvider::getServiceName("Celebrio-Database-IDatabase");
        DDL::execute(__DIR__ . "/$dbname/sql/$dbname.sql");
    }

    private function createRoot() {
        $user = new AlbireoUser();
        $user->setUsername(AclConstants::ROOT_USER);
        $user->setEmail('root@domain');
        $user->setLanguage("cs_CZ");
        $user->setTimezone('+1');
        $password = "albireo";

        return $this->userManagement->addUser($user, $password);
    }

//    public function console($query) {
//        try {
//            $result = dibi::nativeQuery($query);
//            $return = "<div>" . _("Query time:") . " " . dibi::$elapsedTime . "</div>";
//            if ($result instanceof \DibiResult) {
////                if (!$result->fetch()) {
////                    return $return;
////                }
//                $return .= "\n<table class='dbtable'>\n<thead>\n<tr>";
//                foreach ($result->getInfo()->getColumnNames() as $colName) {
//                    $return .= "<th>" . $colName . "</th>";
//                }
//                $return .= "</tr>\n</thead>\n<tbody>\n";
//                foreach ($result as $row) {
//                    $return .= "<tr>";
//                    foreach ($row as $cell) {
//                        $cell = $this->handleNonstringTypes($cell);
//                        $return .= "<td>" . nl2br(htmlspecialchars($cell)) . "</td>";
//                    }
//                    $return .= "</tr>\n";
//                }
//
//                $return .= "</tbody>\n</table>";
//                unset($result);
//            } else {
//                $return.= "<div>" . sprintf(ngettext("%s affected row.", "%s affected rows.", $result), $result) . "</div>";
//            }
//            //return $result->getRowClass();
//            return $return;
//        } catch (\DibiException $e) {
//            return '<div>' . sprintf(_("Error number %s in your SQL command:"), $e->getCode()) . ' <strong>' . $e->getSql() . '</strong> <br /> <strong>' . $e->getMessage() . '</strong> </div>';
//        }
//    }
}